package com.sudo.boot.service.sys.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.If;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sudo.boot.base.constant.SsoConstant;
import com.sudo.boot.base.constant.SysConstant;
import com.sudo.boot.base.constant.enums.EnumYesNo;
import com.sudo.boot.base.exception.BizException;
import com.sudo.boot.base.util.IfFail;
import com.sudo.boot.mapper.sys.entity.*;
import com.sudo.boot.mapper.sys.mapper.*;
import com.sudo.boot.mapper.sys.model.bo.*;
import com.sudo.boot.mapper.sys.model.vo.SysRolePageVo;
import com.sudo.boot.mapper.sys.model.vo.SysRoleSimpleVo;
import com.sudo.boot.mapper.sys.model.vo.SysRoleVo;
import com.sudo.boot.service.sys.SysRoleService;
import com.sudo.boot.service.sys.converter.SysRoleConverter;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.sudo.boot.mapper.sys.entity.table.SysRoleMenuTableDef.SYS_ROLE_MENU;
import static com.sudo.boot.mapper.sys.entity.table.SysRoleTableDef.SYS_ROLE;
import static com.sudo.boot.mapper.sys.entity.table.SysUserRoleTableDef.SYS_USER_ROLE;

/**
 * 系统角色  服务层实现。
 *
 * @author admins
 * @since 2023-10-13
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Resource
    private SysRoleConverter sysRoleConverter;
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private SysUserRoleMapper sysUserRoleMapper;
    @Resource
    private SysRedisService sysRedisService;

    @Override
    public void add(SysRoleBoAdd bo) {

        // 判断用户名是否被占用
        long userCount = mapper.selectCountByCondition(SYS_ROLE.ROLE_CODE.eq(bo.getRoleCode()));
        Assert.isTrue(userCount <= 0, "该角色代码已被占用");

        // 新增角色
        SysRole sysRole = new SysRole();
        sysRole.setRoleCode(bo.getRoleCode());
        sysRole.setRoleName(bo.getRoleName());
        sysRole.setNote(bo.getNote());
        sysRole.setStatus(bo.getStatus());
        IfFail.dealInt(() -> mapper.insert(sysRole));

    }

    @Override
    public void edit(SysRoleBoEdit bo) {
        // 不能编辑超级角色
        SysRole sysRole = mapper.selectOneById(bo.getId());
        if (SysConstant.super_role.equals(sysRole.getRoleCode())) {
            throw new BizException("不能编辑超级角色");
        }

        SysRole editRole = new SysRole();
        editRole.setId(bo.getId());
        editRole.setRoleName(bo.getRoleName());
        editRole.setNote(bo.getNote());
        IfFail.dealInt(() -> mapper.update(editRole));
    }

    @Override
    public void status(SysRoleBoStatus bo) {
        // 不能切换超级角色
        SysRole sysRole = mapper.selectOneById(bo.getId());
        if (SysConstant.super_role.equals(sysRole.getRoleCode())) {
            throw new BizException("不能切换超级角色");
        }

        // 切换角色状态
        SysRole statusRole = new SysRole();
        statusRole.setId(bo.getId());
        statusRole.setStatus(bo.getStatus());
        IfFail.dealInt(() -> mapper.update(statusRole));

        // 清空角色状态缓存
        sysRedisService.removeRoleStatus(sysRole.getRoleCode());
    }

    /**
     * 列出所有基础角色信息
     */
    @Override
    public List<SysRoleSimpleVo> listSimple() {
        QueryWrapper queryWrapper = QueryWrapper.create();
        return mapper.selectListByQueryAs(queryWrapper, SysRoleSimpleVo.class);
    }

    /**
     * 根绝角色代码查询角色信息
     */
    @Override
    public List<SysRole> listRole(List<String> roleCodeList) {
        Assert.notEmpty(roleCodeList, "角色代码集合不能为空");
        return mapper.selectListByCondition(SYS_ROLE.ROLE_CODE.in(roleCodeList));
    }

    @Override
    public Page<SysRolePageVo> page(SysRoleBoPageQuery bo) {
        Page<SysRolePageVo> page = Page.of(bo.getPageNumber(), bo.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(SYS_ROLE.ROLE_NAME.like(bo.getRoleName(), If::hasText))
                .and(SYS_ROLE.ROLE_CODE.like(bo.getRoleCode(),If::hasText))
                .and(SYS_ROLE.STATUS.eq(bo.getStatus(),If::hasText));
        return mapper.paginateAs(page, queryWrapper, SysRolePageVo.class);
    }

    @Override
    public void deleteById(Serializable id) {
        // 1.查询角色
        SysRole sysRole = mapper.selectOneById(id);
        if (sysRole == null) {
            throw new BizException("角色不存在");
        }

        // 2.不能删除超级角色
        if (SysConstant.super_role.equals(sysRole.getRoleCode())) {
            throw new BizException("不能删除超级角色");
        }

        // 3.判断该角色下是否有用户
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(SYS_USER_ROLE)
                .where(SYS_USER_ROLE.ROLE_CODE.eq(sysRole.getRoleCode()));
        List<SysUserRole> userRoleList = mapper.selectListByQueryAs(queryWrapper, SysUserRole.class);
        if (!CollectionUtils.isEmpty(userRoleList)) {
            throw new BizException("该角色下包含用户，不能删除");
        }

        // 4.删除角色
        IfFail.dealInt(() -> mapper.deleteById(id));

        // 5.清除角色状态缓存
        sysRedisService.removeRoleStatus(sysRole.getRoleCode());

        // 6.清除角色菜单、按钮缓存
        sysRedisService.removeMenuOfRole(sysRole.getRoleCode());
        sysRedisService.removeButtonOfRole(sysRole.getRoleCode());

    }

    @Override
    public SysRoleVo detail(Serializable id) {
        SysRole sysRole = mapper.selectOneById(id);
        return sysRoleConverter.toSysRoleVo(sysRole);
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void permission(SysRoleBoMenu bo) {
        // 1.查询角色
        SysRole sysRole = mapper.selectOneById(bo.getRoleId());
        Assert.notNull(sysRole, "角色不存在");
        Assert.isTrue(!SysConstant.super_role.equals(sysRole.getRoleCode()),"不能对超级角色授权");

        // 2.查询菜单
        List<Integer> menuIdList = bo.getMenuIdList().stream().distinct().toList();
        List<SysMenu> sysMenuList = sysMenuMapper.selectListByIds(menuIdList);
        Assert.notEmpty(sysMenuList, "菜单不存在");

        // 3.获取菜单代码集合
        List<String> menuCodeList = sysMenuList.stream().map(SysMenu::getMenuCode).toList();

        // 4.删除原有权限
        sysRoleMenuMapper.deleteByCondition(SYS_ROLE_MENU.ROLE_CODE.eq(sysRole.getRoleCode()));

        // 5.设置新权限
        List<SysRoleMenu> roleMenuList = new ArrayList<>();
        for (String menuCode : menuCodeList) {
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setMenuCode(menuCode);
            sysRoleMenu.setRoleCode(sysRole.getRoleCode());
            roleMenuList.add(sysRoleMenu);
        }
        IfFail.dealInt(() -> sysRoleMenuMapper.insertBatch(roleMenuList));

        // 6.清除角色菜单、按钮缓存
        sysRedisService.removeMenuOfRole(sysRole.getRoleCode());
        sysRedisService.removeButtonOfRole(sysRole.getRoleCode());
    }

    @Override
    public void unbindUser(SysRoleBoUnbindUser bo) {
        // 查询用户
        SysUser sysUser = sysUserMapper.selectOneById(bo.getUserId());
        Assert.notNull(sysUser, "用户不存在");
        Assert.isTrue(!SysConstant.super_user.equals(sysUser.getUsername()), "不可解绑超级用户");

        // 查询角色
        SysRole sysRole = mapper.selectOneById(bo.getRoleId());
        Assert.notNull(sysRole, "角色不存在");

        // 查询用户多角色
        List<SysUserRole> userRoleList = sysUserRoleMapper.selectListByCondition(SYS_USER_ROLE.USERNAME.eq(sysUser.getUsername()));
        Assert.notEmpty(userRoleList, "该用户未绑定角色");

        // 判断用户是否和该角色存在绑定关系
        Optional<SysUserRole> sysUserRole = userRoleList.stream()
                .filter(o -> o.getRoleCode().equals(sysRole.getRoleCode()))
                .findAny();
        Assert.isTrue(sysUserRole.isPresent(), "用户未绑定该角色");
        Assert.isTrue(userRoleList.size() > 1, "唯一角色不能解绑");

        // 判断是否为默认角色，如果是则需要修改默认角色
        if (EnumYesNo.YES.getCode().equals(sysUserRole.get().getDefaultRole())) {
            Optional<SysUserRole> noDefaultRole = userRoleList.stream()
                    .filter(o -> o.getDefaultRole().equals(EnumYesNo.NO.getCode()))
                    .findAny();
            Assert.isTrue(noDefaultRole.isPresent(), "未找到非默认角色");
            noDefaultRole.get().setDefaultRole(EnumYesNo.YES.getCode());
            IfFail.dealInt(() -> sysUserRoleMapper.update(noDefaultRole.get()));
            // 如果用户已经登录，移除用户当前角色缓存
            SaSession userSession = StpUtil.getSessionByLoginId(sysUser.getUsername());
            if (Objects.nonNull(userSession)) {
                userSession.delete(SsoConstant.currRoleCode);
            }
        }

        // 解绑
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(SYS_USER_ROLE.USERNAME.eq(sysUser.getUsername()))
                .and(SYS_USER_ROLE.ROLE_CODE.eq(sysRole.getRoleCode()));
        IfFail.dealInt(() -> sysUserRoleMapper.deleteByQuery(queryWrapper));
    }
}
